package com.xinqi.util.pipeline;

import com.xinqi.common.SystemContents;
import com.xinqi.config.JedisConfig;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author xin麒
 * @date 2023/8/25 16:58
 */
public class JedisPipelineArray {
    private JedisConfig jedisConfig;

    public JedisPipelineArray(@Autowired JedisConfig jedisConfig) {
        this.jedisConfig = jedisConfig;

    }

    private Byte maxCapacity;

    private volatile List<SinglePipeline> pipelines;
    private AtomicBoolean lock;


    private void init() {//以双重检索方式初始化参数
        if (pipelines == null) {
            synchronized (this) {
                if (pipelines == null) {
                    //创建sqlsession的连接数量是主要取决于CPU_COUNT，CPU_COUNT如果没这么大的话，那么sqlsession的连接数应该是可以承受的住的
                    maxCapacity = SystemContents.CPU_COUNT + 1;
                    pipelines = new ArrayList<>(maxCapacity);
                    lock = new AtomicBoolean(true);
                }
            }
        }
    }

    private boolean lock() {
        return lock.compareAndSet(true, false);
    }

    private void unlock() {
        lock.getAndSet(true);
    }

    public SinglePipeline getSinglePipeline() {
        init();
        int index = 0;
        while (true) {
            if (index >= maxCapacity) {
                index = 0;
                continue;
            }
            if (index == pipelines.size()) {
                if (!lock()) {//-----获取锁开始
                    index = 0;//拿不到锁，于是扩容操作就让苦于拿到锁的线程去执行扩容操作
                    continue;
                }
                //以下是拿到锁的对象进行扩容操作
                try {
                    if (pipelines.size() >= maxCapacity) continue;
                    SinglePipeline singlePipeline = new SinglePipeline(jedisConfig);
                    singlePipeline.lock();//这里先提前锁好，不然可能会被其他线程获取到
                    //对于List对象，因为这里只有持有锁的那个线程才可以执行，因此不需要使用Vector等线程安全类型的集合。
                    // 在普通类型加volatile修饰即可，这样子其他线程获取该List对象的属性等值就是最新的了。
                    pipelines.add(singlePipeline);
                    return singlePipeline;
                } finally {
                    unlock();//最后都要执行锁释放操作，如果有异常就让它往外抛吧，不管了。-----释放锁结束
                }
            }
            SinglePipeline singlePipeline = pipelines.get(index);
            if (singlePipeline.lock()) return singlePipeline;
            index++;
        }
    }

    public void helpGC() {
        if (pipelines == null) return;
        for (SinglePipeline singlePipeline : pipelines) singlePipeline.helpGC();
        if (pipelines != null) pipelines.clear();
        pipelines = null;
        jedisConfig = null;
    }
}
